
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Diagnostics;

using Swaf;
using Swaf.Config;

namespace Swaf.Compression
{
    public class CompressionManager : ICompressionManager, IConfigPartner, IConfigHandler
    {
        #region Constants

        private const String CompressionFactory = "CompressionProvider";

        #endregion

        #region Constructors

        public CompressionManager()
        {
        }

        public CompressionManager(IApplication app)
        {
        }

        #endregion

        #region Properties

        private String _compressType = String.Empty;

        public String CompressionType
        {
            get { return _compressType; }
            set { _compressType = value; }
        }

        #endregion

        #region Compression

        public String Compress(Stream inputStream)
        {
            // Read the buffer from the Stream
            byte[] buffer = new byte[inputStream.Length];

            // Read the file into the buffer
            int count = inputStream.Read(buffer, 0, buffer.Length);
            if (count != buffer.Length)
            {
                throw new CompressionException(String.Format("Failure processing stream"));
            }

            // Zip and encode the data
            String encodedCompressedData = String.Empty;
            using (MemoryStream ms = new MemoryStream())
            {
                IClassFactory factory = Application.globalApp.factories[CompressionFactory];
                Debug.Assert(factory != null);

                // Compress the buffer into the memory stream
                using (Stream compressedzipStream = factory.createFromNickName(CompressionType, ms, true, true) as Stream)
                {
                    compressedzipStream.Write(buffer, 0, buffer.Length);
                }

                // Reset the memory stream position to begin base64encoding.
                ms.Position = 0;

                // Base64 encoding the buffer
                encodedCompressedData = Convert.ToBase64String(ms.GetBuffer());
            }

            // return the result
            return encodedCompressedData;
        }

        public String Compress(FileInfo fileInfo)
        {
            if (fileInfo == null)
            {
                throw new CompressionException("Null file was provided, can not perform compression.");
            }

            if (!fileInfo.Exists)
            {
                throw new CompressionException("File does not exist, can not compress contents.");
            }

            try
            {
                String encodedCompressedData = String.Empty;

                // Read the file into a buffer
                using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    encodedCompressedData = Compress(fileStream);
                }

                return encodedCompressedData;
            }
            catch (Exception e)
            {
                throw new CompressionException(String.Format("Unable to compress file [{0}]", fileInfo.FullName), e);
            }
        }

        public String[] Compress(DirectoryInfo dirInfo)
        {
            return Compress(dirInfo, null);
        }

        public String[] Compress(DirectoryInfo dirInfo, String filter)
        {
            // Validate inbound parameters
            if (dirInfo == null)
            {
                throw new CompressionException("Null directory was provided, can not perform compression.");
            }
            if (!dirInfo.Exists)
            {
                throw new CompressionException("Directory provided does not exist, can not perform compression.");
            }

            // Get the files infos for the directory
            FileInfo[] fileInfos = null;
            if (filter == null || filter.Length == 0)
            {
                // Get the files with no filter
                fileInfos = dirInfo.GetFiles();
            }
            else
            {
                // Get the files with the applied filter
                fileInfos = dirInfo.GetFiles(filter);
            }

            if (fileInfos == null || fileInfos.Length == 0)
            {
                throw new CompressionException(String.Format("There are no files associated with the directory [{0}] and filter [{1}]", dirInfo.FullName, filter));
            }

            // Declare the result array of compressed files
            String[] compressedFiles = new String[fileInfos.Length];
            int i = 0;

            foreach (FileInfo fileInfo in fileInfos)
            {
                // Compress each file info
                compressedFiles[i] = Compress(fileInfo);
                ++i;
            }

            return compressedFiles;
        }

        #endregion

        #region Decompression

        public void Decompress(String compressedData, Stream resultStream)
        {
            if (compressedData == null || compressedData == String.Empty)
            {
                throw new CompressionException("Provided compressed compressedStream String is either null or empty, can not decompress.");
            }

            if (resultStream == null)
            {
                throw new CompressionException("Provided result stream is null and can not receive decompressed data");
            }

            try
            {
                // Initially Base64Encoded String, convert to byte []
                byte[] logFileCompressedBytes = Convert.FromBase64String(compressedData);

                // Load byte [] into Memory Stream
                using (MemoryStream ms = new MemoryStream(logFileCompressedBytes))
                {
                    ms.Position = 0;

                    // Get the compression factory
                    IClassFactory compressionFactory = Application.globalApp.factories[CompressionFactory];
                    Debug.Assert(compressionFactory != null);

                    // Load into compression Stream to decompress
                    using (Stream decompressStream = compressionFactory.createFromNickName(CompressionType, ms, false, false) as Stream)
                    {
                        // Declare a buffer to read from the zipstream
                        byte[] buffer = new byte[5500];

                        while (true)
                        {
                            // Read 5000 bytes at a time and write to the file stream
                            int bytesRead = decompressStream.Read(buffer, 0, 5000);
                            if (bytesRead == 0)
                            {
                                break;
                            }

                            // Write to the result stream
                            resultStream.Write(buffer, 0, bytesRead);
                        }

                        // Flush the stream with the applied data
                        resultStream.Flush();
                    }
                }
            }
            catch (Exception e)
            {
                throw new CompressionException("Unable to decompress file to folder", e);
            }
        }

        public void Decompress(String compressedFile, DirectoryInfo dirInfo)
        {
            Decompress(compressedFile, dirInfo, GetDefaultFileNamePrefix(), GetDefaultExtension());
        }

        public void Decompress(String compressedFile, DirectoryInfo dirInfo, String fileNamePrefix, String extension)
        {
            Decompress(compressedFile, dirInfo, GetDefaultFileNamePrefix(), extension, -1);
        }

        public void Decompress(String compressedFile, DirectoryInfo dirInfo, String fileName)
        {
            if (!dirInfo.Exists)
            {
                throw new CompressionException("The directory provided does not exist decompressed file can not be saved.");
            }

            // Create the files stream
            using (FileStream fs = new FileStream(Path.Combine(dirInfo.FullName, fileName), FileMode.Create))
            {
                // Call the stream override of Decompress
                Decompress(compressedFile, fs);
            }
        }

        public void Decompress(String compressedFile, DirectoryInfo dirInfo, String fileNamePrefix, String extension, int count)
        {
            Decompress(compressedFile, dirInfo, String.Format("{0}_{1}.{2}", fileNamePrefix, count, extension));
        }

        public void Decompress(String[] compressedFiles, DirectoryInfo dirInfo)
        {
            Decompress(compressedFiles, dirInfo, GetDefaultFileNamePrefix(), GetDefaultExtension());
        }

        public void Decompress(String[] compressedFiles, DirectoryInfo dirInfo, String fileNamePrefix, String extension)
        {
            if (compressedFiles.Length == 0)
            {
                // No files to compress
                return;
            }

            if (!dirInfo.Exists)
            {
                throw new CompressionException("The directory provided does not exist decompressed files can not be saved.");
            }

            if (fileNamePrefix == null || fileNamePrefix.Length == 0)
            {
                fileNamePrefix = GetDefaultFileNamePrefix();
            }

            int i = 0;
            foreach (String compressedFile in compressedFiles)
            {
                Decompress(compressedFile, dirInfo, fileNamePrefix, extension, i);
                ++i;
            }
        }

        private String GetDefaultFileNamePrefix()
        {
            return DateTime.Now.ToString("yyyyMMdd_HHmmss_fffffff");
        }

        private String GetDefaultExtension()
        {
            return ".txt";
        }

        #endregion

        #region IConfigPartner Members

        public void registerForConfig(IConfigManager config)
        {
            config.registerHandler("...<Compression> <Type>", this);
        }

        public void checkConfigStatus()
        {
            if ((CompressionType == null) || (CompressionType.Length == 0))
            {
                throw new CompressionException("No compression type defined in compression configuration");
            }
        }

        #endregion

        #region IConfigHandler Members

        public void executeBefore(IConfigElement configInfo)
        {
            // Get the factory nickname for the type of configuration that will be being done
            CompressionType = configInfo.Element.InnerText;
        }

        public void executeAfter(IConfigElement configInfo)
        {
        }

        #endregion
    }
}
